/* 
 * File:   XshareLine.cpp
 * Author: minaev
 * 
 * Created on 31 Октябрь 2013 г., 9:45
 */

#include "XshareLine.h"

#include <stddef.h>
#include <string.h>
#include <stdio.h>

/**
 * Create SharedLine Memory
 * Example:
 *      start length of line 
 * |      (startLen)          |
 * |                          |
 * +--------+--------+--------+
 * | block1 | block2 | block3 |
 * +--------+--------+--------+
 * |        |
 * | length |
 *  of block
 * (blockLen)
 * @param blockLen - length of block in bytes
 * @param startLen - start count blocks in line
 */
XshareLine::XshareLine(uint8_t blockLen, uint16_t startLen) {
    this->blockLen = blockLen;
    this->length = 0;
    if (startLen != 0) {
        this->lineMem = new uint8_t[blockLen * startLen];
        this->length = startLen;
    } else {
        this->lineMem = NULL;
    }
}

XshareLine::~XshareLine() {
    delete [] this->lineMem;
}

/**
 * Get pointer of block in line
 * @param nblock - number of block
 * @return pointer of reading block
 */
void* XshareLine::read(uint16_t nblock) {
    if (nblock<this->length) {
        return (void*) (this->lineMem + nblock * blockLen);
    }
    return NULL;
}

/**
 * Write to line
 * @param buf - pointer to writen buffe
 * @param len
 * @param offset
 */
void XshareLine::write(void* buf, uint16_t len, uint16_t offset) {
    if (len + offset <= this->length) {
        memcpy((this->lineMem + offset * blockLen), buf, len * blockLen);
    } else {
        Xmsg::printf("ShareLine Error write len + offset %i <= length %i\n",
                len + offset,
                this->length);
    }
}

int XshareLine::addBlock(const void* block) {
    static uint8_t* tmpbuf;
    tmpbuf = this->lineMem;
    this->lineMem = new uint8_t[(length + 1) * blockLen];
    uint32_t lentmp = length*blockLen;

    if (tmpbuf != NULL) {
        memcpy(this->lineMem, tmpbuf, lentmp);
        delete [] tmpbuf;
    }
    memcpy((void*) (this->lineMem + lentmp), block, blockLen);
    /*for(int i=0;i<blockLen;i++){
        this->lineMem[lentmp+i] = block[i];
    }*/
    length++;
    return length-1;
}

int XshareLine::addBlockWithoutRepeate(const void* block) {
    int numblock = findBlock(block);
    if (numblock == -1) {
        return addBlock(block);
    }else{
        return numblock;
    }
}

/**
 * Find block into shared line
 * @param block - finding block
 * @return position found block in line, or -1 if not found
 */
int XshareLine::findBlock(const void* block) {
    static uint16_t nblk;
    nblk = 0;
    while (this->cmpBlock(block, nblk) == 0) {
        if (++nblk == this->length) {
            return -1;
        }
    }
    return nblk;
}

int XshareLine::removeBlock(uint16_t nblock) {
    if (nblock<this->length) {
        memcpy((void*) (this->lineMem + nblock * blockLen),
                (void*) (this->lineMem + (nblock + 1) * blockLen),
                (length - nblock - 1) * blockLen);

        length--;
        return nblock;
    }else{
        return -1;
    }
}

int XshareLine::removeBlock(void* block) {
    int nblock = findBlock(block);
    if (nblock != -1) {
        return removeBlock(nblock);
    }else{
        return -1;
    }
}

/**
 * Compare block in shareLine with other block
 * @param block - pointer to block for compare
 * @param nblock - number block in line for compare
 * @return 1 - equal, 0 - not equal.
 */
int XshareLine::cmpBlock(const void* block, uint16_t nblock) {
    static uint8_t indx = 0;
    indx = 0;
    if (nblock<this->length) {

        while (*(uint8_t*) ((uint8_t*) block + indx)
                == *(uint8_t*) (((uint8_t*)this->lineMem) + nblock * blockLen + indx)) {
            if (++indx == this->blockLen) {
                return 1;
            }
        }
        return 0;
    }
    return 0;
}

uint8_t XshareLine::getBlockLen() {
    return this->blockLen;
}

uint16_t XshareLine::getLength() {
    return this->length;
}

uint32_t XshareLine::getBytesLength() {
    return this->length * this->blockLen;
}

char* XshareLine::toBytes() {
    static char* bytes;
    static uint32_t lenbytes;
    if (bytes != NULL) {
        delete [] bytes;
    }
    lenbytes = blockLen*length;
    bytes = new char[lenbytes];
    for (int i = 0; i < lenbytes; i++) {
        bytes[i] = ((char*) this->lineMem)[i];
    }
    return bytes;
}

